﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.DataStructures.Maps;
using Algoritmia.DataStructures.Queues;
using Lists = Algoritmia.DataStructures.Lists;
using Sets = Algoritmia.DataStructures.Sets;

namespace Algoritmia.Utils
{
    public static class AlgoritmiaFactory
    {
        #region Lists Factory

        public static class Lists<T>
        {
            public static Lists.IList<T> Default
            {
                get { return new Lists.ArrayList<T>(); }
            }

            public static Lists.IList<T> BuildDefault(IEnumerable<T> items = null,
                                                      EqualityComparison<T> equalityComparison = null)

            {
                return new Lists.ArrayList<T>(items, equalityComparison);
            }
        }

        #endregion

        #region Sets Factory

        public static class Sets<T>
        {
            public static Sets.ISet<T> Default
            {
                get { return new Sets.HashSet<T>(); }
            }

            public static Sets.ISet<T> BuildDefault(IEnumerable<T> items = null)
            {
                return new Sets.HashSet<T>(items);
            }
        }

        #endregion

        #region Maps Factory

        public static class Maps<TKey,TValue>
        {
            public static IMap<TKey,TValue> Default
            {
                get
                {
                    return new HashMap<TKey, TValue>();
                }
            }

            public static IMap<TKey,TValue> BuildDefault(IEnumerable<KeyValuePair<TKey,TValue>> items = null,
                                                         int capacity = 16,
                                                         EqualityComparison<TKey> equalityComparison = null,
                                                         Func<Lists.IList<KeyValuePair<TKey,TValue>>> createContainedLists = null
                                                         )
            {
                return new HashMap<TKey, TValue>(items, capacity, equalityComparison,createContainedLists);
            }
        }

        public static class Maps<T> 
        {
            public static IMap<T,T> Default
            {
                get { return Maps<T, T>.Default; }
            }
            public static IMap<T,T> BuildDefault(IEnumerable<KeyValuePair<T,T>> items = null,
                                                         int capacity = 16,
                                                         EqualityComparison<T> equalityComparison = null,
                                                         Func<Lists.IList<KeyValuePair<T,T>>> createContainedLists = null
                                                         )
            {
                return Maps<T, T>.BuildDefault(items, capacity, equalityComparison, createContainedLists);
            }
        }

        #endregion

        #region Lifos Factory

        public static class Lifos<T>
        {
            public static ILifo<T> Default
            {
                get { return new Lifo<T>(); }
            }

            public static ILifo<T> BuildDefault(IEnumerable<T> items = null,
                        Func<IEnumerable<T>,Lists.IList<T>> createList = null)
            {
                return new Lifo<T>(items,createList);
            }
        }
        #endregion

        #region Fifos Factory
        public static class Fifos<T>
        {
            public static IFifo<T> Default
            {
                get { return new Fifo<T>(); }
            }

            public static IFifo<T> BuildDefault(IEnumerable<T> items = null,
                        Func<IEnumerable<T>, Lists.IList<T>> createList = null)
            {
                return new Fifo<T>(items, createList);
            }
        }
        #endregion

        public static EqualityComparison<T> GetDefaultEqualityComparison<T>(EqualityComparison<T> eqComparison = null)
        {
            if (eqComparison == null)
            {
                var interfaces = typeof(T).GetInterfaces();
                if (interfaces.Contains(typeof(IEquatable<T>)))
                {
                    return (v1, v2) => EqualityComparer<T>.Default.Equals(v1, v2);
                }
                if (interfaces.Contains(typeof(IComparable<T>)))
                {
                    return (v1, v2) => Comparer<T>.Default.Compare(v1, v2) == 0;
                }
                //TODO: Document this behaviour, it can be so weird
                return (v1, v2) => v1.Equals(v2);
            }
            return eqComparison;
        }

        public static Comparison<T> GetDefaultComparison<T>(Comparison<T> comparison = null, string message = null)
        {
            if (comparison == null)
            {
                var interfaces = typeof(T).GetInterfaces();
                if (interfaces.Contains(typeof(IComparable<T>)))
                {
                    return (v1, v2) => Comparer<T>.Default.Compare(v1, v2);
                }
                throw new ArgumentException(message ?? String.Format("Type {0} must implement IComparable<T>", typeof(T).Name));
            }
            return comparison;
        }
    }

}
